#include <dotconf.h>
#include <string.h>
#include <gmetad.h>
#include <ganglia.h>
#include "conf.h"

/* Variables that get filled in by configuration file */
extern Source_t root;
extern hash_t *sources;

gmetad_config_t gmetad_config;

#define GANGLIA_HOSTNAME_LEN  128

static DOTCONF_CB(cb_gridname)
{
    gmetad_config_t *c = (gmetad_config_t*) cmd->option->info;
    debug_msg("Grid name %s", cmd->data.str);
    c->gridname = strdup(cmd->data.str);
    return NULL;
}

static DOTCONF_CB(cb_authority)
{
   /* See gmetad.h for why we record strings this way. */
    debug_msg("Grid authority %s", cmd->data.str);
    root.authority_ptr = 0;
    strcpy(root.strings, cmd->data.str);
    root.stringslen += strlen(root.strings) + 1;
    return NULL;
}

static DOTCONF_CB(cb_all_trusted)
{
   gmetad_config_t *c = (gmetad_config_t*) cmd->option->info;
   debug_msg("All hosts are trusted!");
   c->all_trusted = 1;
   return NULL;
}

static DOTCONF_CB(cb_trusted_hosts)
{
   int i,rv;
   llist_entry *le;
   struct sockaddr_in sa;
   gmetad_config_t *c = (gmetad_config_t*) cmd->option->info;

   for (i = 0; i < cmd->arg_count; i++)
      {
         le = (llist_entry *)malloc(sizeof(llist_entry));
         rv = g_gethostbyname( cmd->data.list[i], &sa, NULL);
         if (!rv) {
            err_msg("Warning: we failed to resolve trusted host name %s", cmd->data.list[i]);
            continue;
         }
         le->val = (char*) malloc(GANGLIA_HOSTNAME_LEN);
         my_inet_ntop(AF_INET, &sa.sin_addr, le->val, GANGLIA_HOSTNAME_LEN);
         llist_add(&(c->trusted_hosts), le);
      }
   return NULL;
}

static DOTCONF_CB(cb_RRAs)
{
  int i;
  gmetad_config_t *c = (gmetad_config_t*) cmd->option->info;
  c->num_RRAs = cmd->arg_count;
  for(i = 0; i < c->num_RRAs; i++)
    {
      c->RRAs[i] = strdup(cmd->data.list[i]);
    }
  return NULL;
}

static DOTCONF_CB(cb_data_source)
{
   unsigned int i;
   data_source_list_t *dslist;
   datum_t key, val, *find;
   int port, rv=0;
   unsigned long step;
   unsigned int source_index=0;
   char *p, *str;
   char *endptr;
   struct sockaddr_in sa;
   gmetad_config_t *c = (gmetad_config_t*) cmd->option->info;

   source_index++;

   debug_msg("Datasource = [%s]", cmd->data.list[0]);

   dslist = (data_source_list_t *) malloc ( sizeof(data_source_list_t) );
   if(!dslist)
      {
         err_quit("Unable to malloc data source list");
      }

   dslist->name = strdup( cmd->data.list[0] );

  /* Set data source step (avg polling interval). Default is 15s.
   * Be careful of the case where the source is an ip address,
   * in which case endptr = '.'
   */
  i=1;
  step=strtoul(cmd->data.list[i], &endptr, 10);
   if (step && *endptr == '\0')
      {
         dslist->step = step;
         i++;
      }
   else
      dslist->step = 15;

   debug_msg("Polling interval for %s is %u sec.", dslist->name, dslist->step);
   c->shortest_step = dslist->step;

   dslist->sources = (g_inet_addr **) malloc( (cmd->arg_count-i) * sizeof(g_inet_addr *) );
   if (! dslist->sources )
      err_quit("Unable to malloc sources array");

   dslist->num_sources = 0;
   dslist->last_good_index = -1;

   for ( ; i< cmd->arg_count; i++)
      {
         str = cmd->data.list[i];

         p = strchr( str, ':' );
         if( p )
            {
               /* Port is specified */
               *p = '\0';
               port = atoi ( p+1 );
            }
         else
            port = 8649;

         rv = g_gethostbyname( cmd->data.list[i], &sa, NULL);
         if (!rv) {
            err_msg("Warning: we failed to resolve data source name %s", cmd->data.list[i]);
            continue;
         }
         str = (char*) malloc(GANGLIA_HOSTNAME_LEN);
         my_inet_ntop(AF_INET, &sa.sin_addr, str, GANGLIA_HOSTNAME_LEN);

         debug_msg("Trying to connect to %s:%d for [%s]", str, port, dslist->name);
         dslist->sources[dslist->num_sources] = (g_inet_addr *) g_inetaddr_new ( str, port );
         if(! dslist->sources[dslist->num_sources])
               err_quit("Unable to create inetaddr [%s:%d] and save it to [%s]", str, port, dslist->name);
         else
               dslist->num_sources++;
         free(str);
      }

   key.data = cmd->data.list[0];
   key.size = strlen(key.data) + 1;

   val.data = &dslist;
   val.size = sizeof(dslist);

   find  = hash_insert( &key, &val, sources );
   if(!find)
         err_quit("Unable to insert list pointer into source hash\n");
   
   debug_msg("Data inserted for [%s] into sources hash", key.data);
   return NULL;
}

static DOTCONF_CB(cb_debug_level)
{
   gmetad_config_t *c = (gmetad_config_t*) cmd->option->info;
   c->debug_level = cmd->data.value;
   debug_msg("Setting the debug level to %d", cmd->data.value);
   return NULL;
}

static DOTCONF_CB(cb_xml_port)
{
   gmetad_config_t *c = (gmetad_config_t*) cmd->option->info;
   debug_msg("Setting xml port to %d", cmd->data.value);
   c->xml_port = cmd->data.value;
   return NULL;
}

static DOTCONF_CB(cb_interactive_port)
{
   gmetad_config_t *c = (gmetad_config_t*) cmd->option->info;
   debug_msg("Setting interactive port to %d", cmd->data.value);
   c->interactive_port = cmd->data.value;
   return NULL;
}

static DOTCONF_CB(cb_server_threads)
{
   gmetad_config_t *c = (gmetad_config_t*) cmd->option->info;
   debug_msg("Setting number of xml server threads to %d", cmd->data.value);
   c->server_threads = cmd->data.value;
   return NULL;
}

static DOTCONF_CB(cb_rrd_rootdir)
{
   gmetad_config_t *c = (gmetad_config_t*) cmd->option->info;
   debug_msg("Setting the RRD Rootdir to %s", cmd->data.str);
   c->rrd_rootdir = strdup (cmd->data.str);
   return NULL;
}

static DOTCONF_CB(cb_setuid_username)
{
   gmetad_config_t *c = (gmetad_config_t*) cmd->option->info;
   debug_msg("Setting setuid username to %s", cmd->data.str);
   c->setuid_username = strdup(cmd->data.str);
   return NULL;
}

static DOTCONF_CB(cb_setuid)
{
   gmetad_config_t *c = (gmetad_config_t*) cmd->option->info;
   c->should_setuid = cmd->data.value;
   return NULL;
}

static DOTCONF_CB(cb_scalable)
{  
   gmetad_config_t *c = (gmetad_config_t*) cmd->option->info;
   debug_msg("Setting scalable = %s", cmd->data.str);
   if (!strcmp(cmd->data.str, "off"))
      c->scalable_mode = 0;
   return NULL;
}

static DOTCONF_CB(cb_case_sensitive_hostnames)
{
   gmetad_config_t *c = (gmetad_config_t*) cmd->option->info;
   c->case_sensitive_hostnames = cmd->data.value;
   return NULL;
}

static FUNC_ERRORHANDLER(errorhandler)
{
   err_quit("gmetad config file error: %s\n", msg);
   return 0;
}

static configoption_t gmetad_options[] =
   {
      {"data_source", ARG_LIST, cb_data_source, &gmetad_config, 0},
      {"gridname", ARG_STR, cb_gridname, &gmetad_config, 0},
      {"authority", ARG_STR, cb_authority, &gmetad_config, 0},
      {"trusted_hosts", ARG_LIST, cb_trusted_hosts, &gmetad_config, 0},
      {"all_trusted", ARG_INT, cb_all_trusted, &gmetad_config, 0},
      {"debug_level",  ARG_INT,  cb_debug_level, &gmetad_config, 0},
      {"xml_port",  ARG_INT, cb_xml_port, &gmetad_config, 0},
      {"interactive_port", ARG_INT, cb_interactive_port, &gmetad_config, 0},
      {"server_threads", ARG_INT, cb_server_threads, &gmetad_config, 0},
      {"rrd_rootdir", ARG_STR, cb_rrd_rootdir, &gmetad_config, 0},
      {"setuid", ARG_TOGGLE, cb_setuid, &gmetad_config, 0},
      {"setuid_username", ARG_STR, cb_setuid_username, &gmetad_config, 0},
      {"scalable", ARG_STR, cb_scalable, &gmetad_config, 0},
      {"RRAs", ARG_LIST, cb_RRAs, &gmetad_config, 0},
      {"case_sensitive_hostnames", ARG_INT, cb_case_sensitive_hostnames, &gmetad_config, 0},
      LAST_OPTION
   };

static void
set_defaults (gmetad_config_t *config)
{
   /* Gmetad defaults */
   config->gridname = "unspecified";
   config->xml_port = 8651;
   config->interactive_port = 8652;
   config->server_threads = 4;
   config->trusted_hosts = NULL;
   config->debug_level = 0;
   config->should_setuid = 1;
   config->setuid_username = "nobody";
   config->rrd_rootdir = "/var/lib/ganglia/rrds";
   config->scalable_mode = 1;
   config->all_trusted = 0;
   config->num_RRAs = 5;
   config->RRAs[0] = "RRA:AVERAGE:0.5:1:244";
   config->RRAs[1] = "RRA:AVERAGE:0.5:24:244";
   config->RRAs[2] = "RRA:AVERAGE:0.5:168:244";
   config->RRAs[3] = "RRA:AVERAGE:0.5:672:244";
   config->RRAs[4] = "RRA:AVERAGE:0.5:5760:374";
   config->case_sensitive_hostnames = 1;
}

int
parse_config_file ( char *config_file )
{
   configfile_t *configfile;

   set_defaults(&gmetad_config);

   configfile = dotconf_create( config_file, gmetad_options, 0, CASE_INSENSITIVE );
   if (!configfile)
      {
         err_quit("Unable to open config file: %s\n", config_file);
      }

   configfile->errorhandler = (dotconf_errorhandler_t) errorhandler;

   if (dotconf_command_loop(configfile) == 0)
      {
         dotconf_cleanup(configfile);
         err_quit("dotconf_command_loop error");
      }
   return 0;
}

int
number_of_datasources ( char *config_file )
{
   int number_of_sources = 0;
   char buf[1024];
   configfile_t *configfile;

   configfile = dotconf_create( config_file, gmetad_options, 0, CASE_INSENSITIVE );
   if (!configfile) {
      err_quit("Unable to open config file: %s\n", config_file);
   }

   while (! dotconf_get_next_line( buf, 1024, configfile ))
      {
         if( strstr( buf, "data_source" ) && (buf[0] != '#') )
            {
               number_of_sources++;
            }
      }
   dotconf_cleanup(configfile);
   return number_of_sources;
}

